(module calculations mzscheme
  (provide make-run-entry
           make-run-date
           make-run-time
           run-time->string
           remove-entry
           pace-minutes/mile
           get-distance
           run-date-sort
           defined-course?
           weekly-mileage
           run-date->ordinal-week
           get-year)
  
  (require (lib "list.ss")
           "string.ss"
           "date.ss")
  
  (define (make-run-entry date tod course distance time hr notes)
    "make-run-entry accepts a date, time of day, course name, distance, time, heart rate, and note argument and returns them as a list."
    (list date tod course distance time hr notes))
  
  (define (make-run-date month day year)
    "make-run-date accepts a month, day, and year as numbers and returns them as a list."
    (list month day year))
  
  (define (make-run-time h m s)
    "make-run-time accepts a number of hours, a number of minutes, and a number of seconds and returns them as a list."
    (list h m s))
  
  (define (run-time->string run-time)
    "run-time->string takes a run-time and returns a string version."
    (string-append (buffer-number (car run-time)) ":"
                   (buffer-number (cadr run-time)) ":"
                   (buffer-number (caddr run-time))))
  
  (define (remove-entry entry log)
    "remove-entry takes an entry argument and a log argument and returns a list with all entries equal to that entry removed."
    (filter (lambda (current)
              (not (equal? current entry)))
            log))
  
  (define (run-time->seconds time)
    "run-time->seconds takes a run-time formatted argument and returns a number of seconds."
    (let ((hour (car time))
          (minute (cadr time))
          (second (caddr time)))
      (+ (* 60 (* 60 hour))
         (* 60 minute)
         second)))
  
  (define (seconds->run-time second)
    "seconds->run-time takes a time in seconds and returns a run-time."
    (let ((hour (floor (/ second (* 60 60)))))
      (let ((minute (floor (/ (- second (* hour 60 60)) 60))))
        (make-run-time hour minute (- second (+ (* hour (* 60 60))
                                                (* minute 60)))))))
  
  (define (pace-seconds/mile distance seconds)
    "pace-seconds/mile takes a distance in miles and a time in seconds and returns the pace in seconds/mile."
    (/ seconds distance))
  
  (define (pace-minutes/mile distance time)
    "pace-minutes/mile takes a distance in miles and a run-time and returns the pace in run-time/mile."
    (map (lambda (x) (inexact->exact (floor x)))
         (seconds->run-time (pace-seconds/mile distance (run-time->seconds time)))))
  
  (define (get-distance entry courses)
    "get-distance takes an entry and returns either the distance of the specified course if the distance is #t or the distance."
    (if (eq? #t (cadddr entry))
        (cadr (assoc (caddr entry) courses))
        (cadddr entry)))
  
  (define (run-date->? x y)
    "run-date->? takes two run dates and returns true if x is later than y or false if y is greater than x."
    (let ((month-x (car x))
          (day-x (cadr x))
          (year-x (caddr x))
          (month-y (car y))
          (day-y (cadr y))
          (year-y (caddr y)))
      (or (> year-x year-y)
          (and (> month-x month-y)
               (not (> year-y year-x)))
          (and (> day-x day-y)
               (not (> month-y month-x))
               (not (> year-y year-x))))))
  
  (define (run-date-sort list)
    "run-date-sort sorts a list of run-entries by run-date."
    (quicksort list (lambda (x y) (run-date->? (car x) (car y)))))
  
  (define (defined-course? course courses)
    "defined-course? returns #t if the course is defined in the list of courses or #f if it is not"
    (not (null? (filter (lambda (x) (equal? (car x) course))
                        courses))))
  
  (define (weekly-mileage start-date log courses)
    "weekly-mileage returns the total mileage for seven days in the log starting on the run-date passed (to the nearest tenth)"
    (rationalize (apply + (map (lambda (x) (get-distance x courses))
                               (filter (lambda (entry)
                                         (week-of? start-date (car entry)))
                                       log)))
                 1/10)))